package cn.zzzhy.core.db.executor;

import cn.zzzhy.core.util.EntityClassWrapper;
import com.google.common.base.Joiner;
import com.google.common.collect.Lists;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.core.SqlTypeValue;
import org.springframework.jdbc.core.StatementCreatorUtils;
import org.springframework.jdbc.support.GeneratedKeyHolder;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

/**
 * author:zzzhy
 * since :15/11/9.
 */
public class SQLExecutor<T> {


    private JdbcTemplate jdbcTemplate;
    private EntityClassWrapper entityClassWrapper;

    public SQLExecutor(JdbcTemplate jdbcTemplate, Class<T> entityClass) {
        this.jdbcTemplate = jdbcTemplate;
        entityClassWrapper = EntityClassWrapper.wrap(entityClass);
    }

    public int insert(T entity) throws IllegalAccessException {

        boolean includeId = entityClassWrapper.isIdPresent(entity);

        final StringBuilder sql = new StringBuilder();
        final List<Object> parameters = buildParameters(entity, includeId);

        sql.append("insert into ").append(entityClassWrapper.getTableName())
                .append("(");
        sql.append(buildColumns(entity, includeId)).append(")");
        sql.append(" values(");
        sql.append(buildColumnPlaceholders(entity, includeId));
        sql.append(")");

        GeneratedKeyHolder generatedKeyHolder = new GeneratedKeyHolder();
        int result = jdbcTemplate.update(new PreparedStatementCreator() {

            public PreparedStatement createPreparedStatement(Connection con)
                    throws SQLException {
                PreparedStatement ps = con.prepareStatement(sql.toString(),
                        Statement.RETURN_GENERATED_KEYS);
                int idx = 1;
                for (Object param : parameters) {
                    StatementCreatorUtils.setParameterValue(ps, idx++,
                            SqlTypeValue.TYPE_UNKNOWN, param);
                }
                return ps;
            }
        }, generatedKeyHolder);

        Number key = generatedKeyHolder.getKey();
        if (key != null) {
            EntityClassWrapper.ColumnField idField = entityClassWrapper.getIdColumnField();
            if (idField == null) {
                throw new IllegalArgumentException("id column is required!");
            }
            Class<?> keyClass = idField.getField().getType();
            if (keyClass == Long.class || keyClass == Long.TYPE) {
                idField.set(entity, key.longValue());
            } else {
                idField.set(entity, key.intValue());
            }
        }

        return result;
    }

    private String buildColumnPlaceholders(T entity, boolean includeId) throws IllegalAccessException {
        List<String> list = Lists.newArrayList();

        for (EntityClassWrapper.ColumnField field : entityClassWrapper.getColumnFields()) {
            if (field.isId() && !includeId) {
                continue;
            }

            if (field.get(entity) == null) {
                continue;
            }

            list.add("?");
        }

        return Joiner.on(",").join(list);
    }

    private String buildColumns(T entity, boolean includeId) throws IllegalAccessException {
        List<String> list = Lists.newArrayList();

        for (EntityClassWrapper.ColumnField field : entityClassWrapper.getColumnFields()) {
            if (field.isId() && !includeId) {
                continue;
            }

            if (field.get(entity) == null) {
                continue;
            }

            list.add(field.getColumnName());
        }

        return Joiner.on(",").join(list);
    }

    private List<Object> buildParameters(T entity, boolean includeId) throws IllegalAccessException {
        List<Object> list = Lists.newArrayList();

        for (EntityClassWrapper.ColumnField field : entityClassWrapper.getColumnFields()) {
            if (field.isId() && !includeId) {
                continue;
            }

            if (field.get(entity) == null) {
                continue;
            }
            Object value = field.getJdbcValue(entity);
            list.add(value);
        }

        return list;
    }

}
